using System;
using System.Data;
using System.Configuration;
using System.Collections.Generic;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Drawing;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Reflection;
using System.IO;
using System.Diagnostics;

[assembly: WebResource("Winthusiasm.HtmlEditor.Images.backcolor.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.bold.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.bulletedlist.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.center.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.forecolor.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.image.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.indent.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.italic.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.justify.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.left.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.link.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.orderedlist.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.outdent.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.pixel.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.right.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.subscript.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.superscript.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.rule.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.underline.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.design.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.html.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.view.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.save.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.Images.new.gif", "img/gif")]
[assembly: WebResource("Winthusiasm.HtmlEditor.HtmlEditor.js", "text/javascript")]

namespace Winthusiasm.HtmlEditor
{
    /// <summary>
    /// Summary description for HtmlEditor
    /// </summary>
    public class HtmlEditor : CompositeControl, IScriptControl
    {
        protected string text = String.Empty;
        protected HiddenField htmlencodedText;
        protected HtmlGenericControl htmlEditor;
        protected HtmlGenericControl designEditor;
        protected HtmlGenericControl htmlArea;
        protected HtmlGenericControl designArea;
        protected HtmlGenericControl toolbarContainer;
        protected HtmlGenericControl htmlTab;
        protected HtmlGenericControl designTab;
        protected Button saveButton;
        protected UpdatePanel updatePanel;
        protected bool updateHtml = false;
        protected ScriptManager sm;
        protected string _editorFontFamily;

        public enum Mode
        {
            Design,
            Html
        }

        public enum Direction
        {
            NotSet,
            LeftToRight,
            RightToLeft
        }

        public enum ToggleModeType
        {
            Tabs,
            ToggleButton,
            Buttons,
            None
        }

        

        [Description("The HTML text to edit.")]
        [Category("Data")]
        public string Text
        {
            get
            {
                if (htmlencodedText != null)
                    text = ClientDecode(htmlencodedText.Value);

                return text;
            }
            set 
            {
                text = value;

                if (htmlencodedText == null) return;

                string html = ClientEncode(text);
                htmlencodedText.Value = html;

                if (Page.IsPostBack)
                {
                    updatePanel.Update();
                    updateHtml = true;
                }
            }
        }

        [Description("Width of the control.")]
        [Category("Layout")]
        [DefaultValue("500px")]
        public override Unit Width
        {
            get 
            {
                if (base.Width.IsEmpty)
                    base.Width = Unit.Pixel(500);

                return base.Width; 
            }
            set 
            {
                base.Width = value; 
            }
        }

        [Description("Height of the control.")]
        [Category("Layout")]
        [DefaultValue("300px")]
        public override Unit Height
        {
            get 
            {
                if (base.Height.IsEmpty)
                    base.Height = Unit.Pixel(300);

                return base.Height; 
            }
            set
            {
                if (value.Value < 300) value = Unit.Pixel(300);
                base.Height = value;
            }
        }

        protected const int defaultEditorBorderSize = 4;
        
        [Description("Size of the border around the editor area.")]
        [Category("Layout")]
        [DefaultValue(defaultEditorBorderSize)]
        public int EditorBorderSize
        {
            get
            {
                if (ViewState["EditorBorderSize"] != null)
                    return (int)ViewState["EditorBorderSize"];
                else
                    return defaultEditorBorderSize;
            }
            set { ViewState["EditorBorderSize"] = value; }
        }

        [Description("Background color of the control.")]
        [Category("Appearance")]
        [DefaultValue("White")]
        public override Color BackColor
        {
            get 
            {
                if (base.BackColor.IsEmpty)
                    base.BackColor = ColorTranslator.FromHtml("White");
                
                return base.BackColor; 
            }
            set { base.BackColor = value; }
        }

        [Description("Default color of the text in the document.")]
        [Category("Appearance")]
        [DefaultValue("Black")]
        public Color EditorForeColor
        {
            get
            {
                if (ViewState["EditorForeColor"] != null)
                    return (Color)ViewState["EditorForeColor"];
                else
                    return ColorTranslator.FromHtml("Black");
            }
            set { ViewState["EditorForeColor"] = value; }
        }

        [Description("Background color of the document.")]
        [Category("Appearance")]
        [DefaultValue("White")]
        public Color EditorBackColor
        {
            get
            {
                if (ViewState["EditorBackColor"] != null)
                    return (Color)ViewState["EditorBackColor"];
                else
                    return ColorTranslator.FromHtml("White");
            }
            set { ViewState["EditorBackColor"] = value; }
        }

        [Description("Color of the border around the editor area.")]
        [Category("Appearance")]
        [DefaultValue("SteelBlue")]
        public Color EditorBorderColor
        {
            get
            {
                if (ViewState["EditorBorderColor"] != null)
                    return (Color)ViewState["EditorBorderColor"];
                else
                    return ColorTranslator.FromHtml("SteelBlue");
            }
            set { ViewState["EditorBorderColor"] = value; }
        }

        [Description("Color of the toolbars and document borders.")]
        [Category("Appearance")]
        [DefaultValue("SteelBlue")]
        public Color ToolbarColor
        {
            get
            {
                if (ViewState["ToolbarColor"] != null)
                    return (Color)ViewState["ToolbarColor"];
                else
                    return ColorTranslator.FromHtml("SteelBlue");
            }
            set { ViewState["ToolbarColor"] = value; }
        }

        [Description("Color of the tab text.")]
        [Category("Appearance")]
        [DefaultValue("LightYellow")]
        public Color TabForeColor
        {
            get
            {
                if (ViewState["TabForeColor"] != null)
                    return (Color)ViewState["TabForeColor"];
                else
                    return ColorTranslator.FromHtml("LightYellow");
            }
            set { ViewState["TabForeColor"] = value; }
        }

        [Description("Background color of the unselected tab.")]
        [Category("Appearance")]
        [DefaultValue("LightSteelBlue")]
        public Color TabBackColor
        {
            get
            {
                if (ViewState["TabBackColor"] != null)
                    return (Color)ViewState["TabBackColor"];
                else
                    return ColorTranslator.FromHtml("LightSteelBlue");
            }
            set { ViewState["TabBackColor"] = value; }
        }

        [Description("Background color of the selected tab.")]
        [Category("Appearance")]
        [DefaultValue("SteelBlue")]
        public Color SelectedTabBackColor
        {
            get
            {
                if (ViewState["SelectedTabBackColor"] != null)
                    return (Color)ViewState["SelectedTabBackColor"];
                else
                    return ColorTranslator.FromHtml("SteelBlue");
            }
            set { ViewState["SelectedTabBackColor"] = value; }
        }

        [Description("Background color of the tab on mouseover.")]
        [Category("Appearance")]
        [DefaultValue("LightBlue")]
        public Color TabMouseOverColor
        {
            get
            {
                if (ViewState["TabMouseOverColor"] != null)
                    return (Color)ViewState["TabMouseOverColor"];
                else
                    return ColorTranslator.FromHtml("LightBlue");
            }
            set { ViewState["TabMouseOverColor"] = value; }
        }

        [Description("Background color of the tabbar.")]
        [Category("Appearance")]
        [DefaultValue("White")]
        public Color TabbarBackColor
        {
            get
            {
                if (ViewState["TabbarBackColor"] != null)
                    return (Color)ViewState["TabbarBackColor"];
                else
                    return ColorTranslator.FromHtml("White");
            }
            set { ViewState["TabbarBackColor"] = value; }
        }

        [Description("Background color of the toolbar buttons on mouseover.")]
        [Category("Appearance")]
        [DefaultValue("LightYellow")]
        public Color ButtonMouseOverColor
        {
            get
            {
                if (ViewState["ButtonMouseOverColor"] != null)
                    return (Color)ViewState["ButtonMouseOverColor"];
                else
                    return ColorTranslator.FromHtml("LightYellow");
            }
            set { ViewState["ButtonMouseOverColor"] = value; }
        }

        [Description("Border color of the toolbar buttons on mouseover.")]
        [Category("Appearance")]
        [DefaultValue("Black")]
        public Color ButtonMouseOverBorderColor
        {
            get
            {
                if (ViewState["ButtonMouseOverBorderColor"] != null)
                    return (Color)ViewState["ButtonMouseOverBorderColor"];
                else
                    return ColorTranslator.FromHtml("Black");
            }
            set { ViewState["ButtonMouseOverBorderColor"] = value; }
        }

        [Description("Convert output to XHTML.")]
        [Category("Behavior")]
        [DefaultValue(true)]
        public bool OutputXHTML
        {
            get
            {
                if (ViewState["OutputXHTML"] != null)
                    return (bool)ViewState["OutputXHTML"];
                else
                    return true;
            }
            set { ViewState["OutputXHTML"] = value; }
        }

        [Description("Convert deprecated syntax to standards (OutputXHTML must be true).")]
        [Category("Behavior")]
        [DefaultValue(true)]
        public bool ConvertDeprecatedSyntax
        {
            get
            {
                if (ViewState["ConvertDeprecatedSyntax"] != null)
                    return (bool)ViewState["ConvertDeprecatedSyntax"];
                else
                    return true;
            }
            set { ViewState["ConvertDeprecatedSyntax"] = value; }
        }

        [Description("Convert top-level paragraph elements inserted in Internet Explorer to appropriate break and/or div elements (OutputXHTML and ConvertDeprecatedSyntax must be true).")]
        [Category("Behavior")]
        [DefaultValue(false)]
        public bool ConvertParagraphs
        {
            get
            {
                if (ViewState["ConvertParagraphs"] != null)
                    return (bool)ViewState["ConvertParagraphs"];
                else
                    return false;
            }
            set { ViewState["ConvertParagraphs"] = value; }
        }

        [Description("Replace nbsp entities inserted in Internet Explorer with spaces (OutputXHTML must be true).")]
        [Category("Behavior")]
        [DefaultValue(false)]
        public bool ReplaceNoBreakSpace
        {
            get
            {
                if (ViewState["ReplaceNoBreakSpace"] != null)
                    return (bool)ViewState["ReplaceNoBreakSpace"];
                else
                    return false;
            }
            set { ViewState["ReplaceNoBreakSpace"] = value; }
        }

        [Description("Display an asterick on the current tab whenever the text has been modified and not yet saved.")]
        [Category("Behavior")]
        [DefaultValue(true)]
        public bool ShowModifiedAsterick
        {
            get
            {
                if (ViewState["ShowModifiedAsterick"] != null)
                    return (bool)ViewState["ShowModifiedAsterick"];
                else
                    return true;
            }
            set { ViewState["ShowModifiedAsterick"] = value; }
        }

        [Description("Add newline character after block tags in the HTML mode editor (OutputXHTML must be true, IE only).")]
        [Category("Behavior")]
        [DefaultValue(true)]
        public bool FormatHtmlMode
        {
            get
            {
                if (ViewState["FormatHtmlMode"] != null)
                    return (bool)ViewState["FormatHtmlMode"];
                else
                    return true;
            }
            set { ViewState["FormatHtmlMode"] = value; }
        }

        protected const string defaultAllowedTags = "p,b,i,u,em,big,small,div,img,span,blockquote,code,pre,br,hr,ul,ol,li,del,ins,strong,a,font,dl,dd,dt,h1,h2,h3,h4,h5,h6,address";

        [Description("Comma delimited list of allowed tags (OutputXHTML must be true).")]
        [Category("Behavior")]
        [DefaultValue(defaultAllowedTags)]
        public string AllowedTags
        {
            get
            {
                if (ViewState["AllowedTags"] == null)
                {
                    ViewState["AllowedTags"] = defaultAllowedTags;
                }

                return (string)ViewState["AllowedTags"];
            }
            set
            {
                if (value != null)
                {
                    string[] allowedTags = value.Split(',');
                    if (allowedTags.Length > 0)
                        ViewState["AllowedTags"] = BuildCommaDelimitedStringFromList(allowedTags, true);
                }
            }
        }

        protected const string defaultAllowedAttributes = "class,href,target,src,align,color,size,style,face,dir";

        [Description("Comma delimited list of allowed attributes (OutputXHTML must be true).")]
        [Category("Behavior")]
        [DefaultValue(defaultAllowedAttributes)]
        public string AllowedAttributes
        {
            get
            {
                if (ViewState["AllowedAttributes"] == null)
                {
                    ViewState["AllowedAttributes"] = defaultAllowedAttributes;
                }

                return (string)ViewState["AllowedAttributes"];
            }
            set
            {
                if (value != null)
                {
                    string[] allowedAttributes = value.Split(',');
                    if (allowedAttributes.Length > 0)
                        ViewState["AllowedAttributes"] = BuildCommaDelimitedStringFromList(allowedAttributes, true);
                }
            }
        }

        protected const string defaultNoScriptAttributes = "href,src";

        [Description("Comma delimited list of no script attributes (OutputXHTML must be true).")]
        [Category("Behavior")]
        [DefaultValue(defaultNoScriptAttributes)]
        public string NoScriptAttributes
        {
            get
            {
                if (ViewState["NoScriptAttributes"] == null)
                {
                    ViewState["NoScriptAttributes"] = defaultNoScriptAttributes;
                }

                return (string)ViewState["NoScriptAttributes"];
            }
            set
            {
                if (value != null)
                {
                    string[] noScriptAttributes = value.Split(',');
                    if (noScriptAttributes.Length > 0)
                        ViewState["NoScriptAttributes"] = BuildCommaDelimitedStringFromList(noScriptAttributes, true);
                }
            }
        }

        [Description("Initial mode on startup.")]
        [Category("Behavior")]
        [DefaultValue(HtmlEditor.Mode.Design)]
        public HtmlEditor.Mode InitialMode
        {
            get
            {
                if (ViewState["InitialMode"] != null)
                    return (HtmlEditor.Mode)ViewState["InitialMode"];
                else
                    return HtmlEditor.Mode.Design;
            }
            set { ViewState["InitialMode"] = value; }
        }

        [Description("Determines if Design Mode is editable.")]
        [Category("Behavior")]
        [DefaultValue(true)]
        public bool DesignModeEditable
        {
            get
            {
                if (ViewState["DesignModeEditable"] != null)
                    return (bool)ViewState["DesignModeEditable"];
                else
                    return true;
            }
            set { ViewState["DesignModeEditable"] = value; }
        }

        [Description("Determines if Html Mode is editable.")]
        [Category("Behavior")]
        [DefaultValue(true)]
        public bool HtmlModeEditable
        {
            get
            {
                if (ViewState["HtmlModeEditable"] != null)
                    return (bool)ViewState["HtmlModeEditable"];
                else
                    return true;
            }
            set { ViewState["HtmlModeEditable"] = value; }
        }

        [Description("Determines the mechanism for switching between modes.")]
        [Category("Behavior")]
        [DefaultValue(HtmlEditor.ToggleModeType.Tabs)]
        public HtmlEditor.ToggleModeType ToggleMode
        {
            get
            {
                if (ViewState["ToggleMode"] != null)
                    return (HtmlEditor.ToggleModeType)ViewState["ToggleMode"];
                else
                    return HtmlEditor.ToggleModeType.Tabs;
            }
            set { ViewState["ToggleMode"] = value; }
        }

        [Description("Determines if the toolbar is docked to the editor.")]
        [Category("Behavior")]
        [DefaultValue(true)]
        public bool ToolbarDocked
        {
            get
            {
                if (ViewState["ToolbarDocked"] != null)
                    return (bool)ViewState["ToolbarDocked"];
                else
                    return true;
            }
            set { ViewState["ToolbarDocked"] = value; }
        }

        protected const string defaultToolbarClass = "";
        
        [CssClassProperty()]
        [Category("Behavior")]
        public string EditorFontFamily
        {
            set
            {
                _editorFontFamily = value;
            }
            get
            {
                return _editorFontFamily;
            }
        }

        [Description("Assigns a CSS class to the toolbar (ToolbarDocked must be false).")]
        [Category("Behavior")]
        [DefaultValue(defaultToolbarClass)]
        public string ToolbarClass
        {
            get
            {
                if (ViewState["ToolbarClass"] != null)
                    return (string)ViewState["ToolbarClass"];
                else
                    return defaultToolbarClass;
            }
            set { ViewState["ToolbarClass"] = value.Trim(); }
        }

        [Description("Css file to link for Design mode.")]
        [Category("Appearance")]
        [DefaultValue("")]
        public string DesignModeCss
        {
            get
            {
                if (ViewState["DesignModeCss"] != null)
                    return (string)ViewState["DesignModeCss"];
                else
                    return String.Empty;
            }
            set { ViewState["DesignModeCss"] = value; }
        }

        [Description("Text direction in Design mode.")]
        [Category("Behavior")]
        [DefaultValue(HtmlEditor.Direction.NotSet)]
        public HtmlEditor.Direction TextDirection
        {
            get
            {
                if (ViewState["TextDirection"] != null)
                    return (HtmlEditor.Direction)ViewState["TextDirection"];
                else
                    return HtmlEditor.Direction.NotSet;
            }
            set { ViewState["TextDirection"] = value; }
        }

        [Description("Client-side handler for modified flag change.")]
        [Category("Behavior")]
        public String ModifiedChanged
        {
            get
            {
                if (ViewState["ModifiedChanged"] != null)
                    return (String)ViewState["ModifiedChanged"];
                else
                    return String.Empty;
            }
            set { ViewState["ModifiedChanged"] = value; }
        }

        [Description("Client-side handler for design mode context change.")]
        [Category("Behavior")]
        public String ContextChanged
        {
            get
            {
                if (ViewState["ContextChanged"] != null)
                    return (String)ViewState["ContextChanged"];
                else
                    return String.Empty;
            }
            set { ViewState["ContextChanged"] = value; }
        }

        protected const string defaultToolbarButtonsTop = "ForeColor,BackColor";

        [Description("Comma delimited list of top toolbar buttons.")]
        [Category("Behavior")]
        [DefaultValue(defaultToolbarButtonsTop)]
        public string ToolbarButtonsTop
        {
            get
            {
                if (ViewState["ToolbarButtonsTop"] == null)
                {
                    ViewState["ToolbarButtonsTop"] = defaultToolbarButtonsTop;
                }

                return (string)ViewState["ToolbarButtonsTop"];
            }
            set
            {
                if (value != null)
                {
                    string[] toolbarButtonsTop = value.Split(',');
                    if (toolbarButtonsTop.Length > 0)
                        ViewState["ToolbarButtonsTop"] = BuildCommaDelimitedStringFromList(toolbarButtonsTop, false);
                }
            }
        }

        protected const string defaultToolbarButtonsBottom = "Bold,Italic,Underline,Left,Center,Right,Justify,OrderedList,BulletedList,Indent,Outdent,Rule,Subscript,Superscript,Link,Image";

        [Description("Comma delimited list of bottom toolbar buttons.")]
        [Category("Behavior")]
        [DefaultValue(defaultToolbarButtonsBottom)]
        public string ToolbarButtonsBottom
        {
            get
            {
                if (ViewState["ToolbarButtonsBottom"] == null)
                {
                    ViewState["ToolbarButtonsBottom"] = defaultToolbarButtonsBottom;
                }

                return (string)ViewState["ToolbarButtonsBottom"];
            }
            set
            {
                if (value != null)
                {
                    string[] toolbarButtonsBottom = value.Split(',');
                    if (toolbarButtonsBottom.Length > 0)
                        ViewState["ToolbarButtonsBottom"] = BuildCommaDelimitedStringFromList(toolbarButtonsBottom, false);
                }
            }
        }

        protected const string defaultToolbarSelectLists = "Format,Font,Size";

        [Description("Comma delimited list of toolbar select lists.")]
        [Category("Behavior")]
        [DefaultValue(defaultToolbarSelectLists)]
        public string ToolbarSelectLists
        {
            get
            {
                if (ViewState["ToolbarSelectLists"] == null)
                {
                    ViewState["ToolbarSelectLists"] = defaultToolbarSelectLists;
                }

                return (string)ViewState["ToolbarSelectLists"];
            }
            set
            {
                if (value != null)
                {
                    string[] toolbarSelectLists = value.Split(',');
                    if (toolbarSelectLists.Length > 0)
                        ViewState["ToolbarSelectLists"] = BuildCommaDelimitedStringFromList(toolbarSelectLists, false);
                }
            }
        }

        public string ToolbarID
        {
            get { return this.ToolbarContainerID; }
        }

        public void Revert()
        {
            Text = Text;
        }
        
        protected int EditorWidth
        {
            get
            {
                if (ViewState["EditorWidth"] != null)
                    return (int)ViewState["EditorWidth"];
                else
                    return 0;
            }
            set { ViewState["EditorWidth"] = value; }
        }

        protected int EditorHeight
        {
            get
            {
                if (ViewState["EditorHeight"] != null)
                    return (int)ViewState["EditorHeight"];
                else
                    return 0;
            }
            set { ViewState["EditorHeight"] = value; }
        }

        protected int ToolbarHeight
        {
            get
            {
                if (ViewState["ToolbarHeight"] != null)
                    return (int)ViewState["ToolbarHeight"];
                else
                    return 25;
            }
            set { ViewState["ToolbarHeight"] = value; }
        }

        protected int ToolbarPadding
        {
            get { return Math.Max(defaultEditorBorderSize, EditorBorderSize); }
        }

        protected int AdditionalToolbarPadding
        {
            get { return ToolbarPadding - EditorBorderSize; }
        }

        protected int TabbarHeight
        {
            get
            {
                if (ViewState["TabbarHeight"] != null)
                    return (int)ViewState["TabbarHeight"];
                else
                    return 40;
            }
            set { ViewState["TabbarHeight"] = value; }
        }

        protected int TabWidth
        {
            get
            {
                if (ViewState["TabWidth"] != null)
                    return (int)ViewState["TabWidth"];
                else
                    return 85;
            }
            set { ViewState["TabWidth"] = value; }
        }

        protected string HtmlEncodedTextID
        {
            get
            {
                if (ViewState["HtmlEncodedTextID"] != null)
                    return (string)ViewState["HtmlEncodedTextID"];
                else
                    return String.Empty;
            }
            set { ViewState["HtmlEncodedTextID"] = value; }
        }

        protected string HtmlEditorID
        {
            get
            {
                if (ViewState["HtmlEditorID"] != null)
                    return (string)ViewState["HtmlEditorID"];
                else
                    return String.Empty;
            }
            set { ViewState["HtmlEditorID"] = value; }
        }

        protected string DesignEditorID
        {
            get
            {
                if (ViewState["DesignEditorID"] != null)
                    return (string)ViewState["DesignEditorID"];
                else
                    return String.Empty;
            }
            set { ViewState["DesignEditorID"] = value; }
        }

        protected string ToolbarContainerID
        {
            get
            {
                if (ViewState["ToolbarContainerID"] != null)
                    return (string)ViewState["ToolbarContainerID"];
                else
                    return String.Empty;
            }
            set { ViewState["ToolbarContainerID"] = value; }
        }

        protected string HtmlAreaID
        {
            get
            {
                if (ViewState["HtmlAreaID"] != null)
                    return (string)ViewState["HtmlAreaID"];
                else
                    return String.Empty;
            }
            set { ViewState["HtmlAreaID"] = value; }
        }

        protected string DesignAreaID
        {
            get
            {
                if (ViewState["DesignAreaID"] != null)
                    return (string)ViewState["DesignAreaID"];
                else
                    return String.Empty;
            }
            set { ViewState["DesignAreaID"] = value; }
        }

        protected string HtmlTabID
        {
            get
            {
                if (ViewState["HtmlTabID"] != null)
                    return (string)ViewState["HtmlTabID"];
                else
                    return String.Empty;
            }
            set { ViewState["HtmlTabID"] = value; }
        }

        protected string DesignTabID
        {
            get
            {
                if (ViewState["DesignTabID"] != null)
                    return (string)ViewState["DesignTabID"];
                else
                    return String.Empty;
            }
            set { ViewState["DesignTabID"] = value; }
        }

        protected string SaveButtonID
        {
            get
            {
                if (ViewState["SaveButtonID"] != null)
                    return (string)ViewState["SaveButtonID"];
                else
                    return String.Empty;
            }
            set { ViewState["SaveButtonID"] = value; }
        }

        protected string ToolbarIDs
        {
            get
            {
                if (ViewState["ToolbarIDs"] != null)
                    return (string)ViewState["ToolbarIDs"];
                else
                    return String.Empty;
            }
            set { ViewState["ToolbarIDs"] = value; }
        }

        protected string ToolbarData
        {
            get
            {
                if (ViewState["ToolbarData"] != null)
                    return (string)ViewState["ToolbarData"];
                else
                    return String.Empty;
            }
            set { ViewState["ToolbarData"] = value; }
        }

        protected string BuildCommaDelimitedStringFromList(string[] list, bool forceLowerCase)
        {
            string s = String.Empty;
            for (int i = 0; i < list.Length; i++)
            {
                if (i > 0) s += ",";
                
                string item = list[i].Trim();
                if (forceLowerCase) item = item.ToLower();

                s += item;
            }
            
            return s;
        }

        protected string ToBackColor
        {
            get { return ColorTranslator.ToHtml(this.BackColor); }
        }

        protected string ToEditorForeColor
        {
            get { return ColorTranslator.ToHtml(this.EditorForeColor); }
        }

        protected string ToEditorBackColor
        {
            get { return ColorTranslator.ToHtml(this.EditorBackColor); }
        }

        protected string ToEditorBorderColor
        {
            get { return ColorTranslator.ToHtml(this.EditorBorderColor); }
        }

        protected string ToToolbarColor
        {
            get { return ColorTranslator.ToHtml(this.ToolbarColor); }
        }

        protected string ToTabForeColor
        {
            get { return ColorTranslator.ToHtml(this.TabForeColor); }
        }

        protected string ToTabBackColor
        {
            get { return ColorTranslator.ToHtml(this.TabBackColor); }
        }

        protected string ToSelectedTabBackColor
        {
            get { return ColorTranslator.ToHtml(this.SelectedTabBackColor); }
        }

        protected string ToTabbarBackColor
        {
            get { return ColorTranslator.ToHtml(this.TabbarBackColor); }
        }

        protected string ToButtonMouseOverColor
        {
            get { return ColorTranslator.ToHtml(this.ButtonMouseOverColor); }
        }

        protected override HtmlTextWriterTag TagKey
        {
            get { return HtmlTextWriterTag.Div; }
        }

        protected string FindScriptObject
        {
            get { return "$find('" + this.ClientID + "')"; }
        }

        protected string GetSafeScriptMethod(string method)
        {
            return "var e = " + FindScriptObject + "; if (e) { e." + method + "; }";
        }

        protected string GetWebResourceUrl(string resource, string imgName)
        {
            if (!this.DesignMode)
                return Page.ClientScript.GetWebResourceUrl(this.GetType(), resource);
            else
                return GetLocalResourceUrl(resource, imgName);
        }

        protected string ClientEncode(string html)
        {
            html = html.Replace("&quot;", "&quotx;");
            html = html.Replace("\"", "&quot;");
            html = html.Replace("&amp;", "&ampx;");
            html = html.Replace("&", "&amp;");
            html = html.Replace("&lt;", "&ltx;");
            html = html.Replace("<", "&lt;");
            html = html.Replace("&gt;", "&gtx;");
            html = html.Replace(">", "&gt;");

            return html;
        }

        protected string ClientDecode(string html)
        {
            html = html.Replace("&gt;", ">");
            html = html.Replace("&gtx;", "&gt;");
            html = html.Replace("&lt;", "<");
            html = html.Replace("&ltx;", "&lt;");
            html = html.Replace("&amp;", "&");
            html = html.Replace("&ampx;", "&amp;");
            html = html.Replace("&quot;", "\"");
            html = html.Replace("&quotx;", "&quot;");

            return html;
        }

        protected HtmlImage CreateRoundedCorner(bool left, bool top, string color)
        {
            string leftRight = left ? "left" : "right";
            string topBottom = top ? "top" : "bottom";
            string leftRightPosition = "0px";
            string topBottomPosition = "0px";

            if (!this.DesignMode)
            {
                bool ie6 = this.Page.Request.Browser.IsBrowser("IE") &&
                           this.Page.Request.Browser.MajorVersion < 7;

                if ((ie6) && (!top) && (!left)) leftRightPosition = "-1px";
            }
            
            HtmlImage corner = new HtmlImage();
            string imgName = "pixel.gif";
            string resource = string.Format("Winthusiasm.HtmlEditor.Images.{0}", imgName);
            corner.Src = GetWebResourceUrl(resource, imgName);
            corner.Height = 1;
            corner.Width = 1;
            corner.Alt = "";
            corner.Style["position"] = "absolute";
            corner.Style[leftRight] = leftRightPosition;
            corner.Style[topBottom] = topBottomPosition;
            corner.Style["margin"] = "0px";
            corner.Style["padding"] = "0px";
            corner.Style["background-color"] = color;
            corner.Style["font-size"] = "0px";

            return corner;
        }

        protected string[] GetStringList(string commaDelimitedList)
        {
            string[] stringList = commaDelimitedList.Split(',');
            if (stringList.Length == 1 && stringList[0] == String.Empty)
                return new List<String>().ToArray();

            return stringList;
        }
        
        protected SelectInfo.SelectList CreateSelectList(string name)
        {
            SelectInfo.SelectList selectList = new SelectInfo.SelectList();
            
            switch (name)
            {
                case "Format" :
                    selectList.Add("Normal", "p");
                    if (!this.ConvertParagraphs) selectList.Add("Paragraph", "p");
                    selectList.Add("Heading 1", "h1");
                    selectList.Add("Heading 2", "h2");
                    selectList.Add("Heading 3", "h3");
                    selectList.Add("Heading 4", "h4");
                    selectList.Add("Heading 5", "h5");
                    selectList.Add("Heading 6", "h6");
                    selectList.Add("Address", "address");
                    selectList.Add("Formatted", "pre");
                    break;
                case "Font" :
                    selectList.Add("Font", "");
                    selectList.Add("Arial", "Arial");
                    selectList.Add("Courier New", "Courier New");
                    selectList.Add("Garamond", "Garamond");
                    selectList.Add("Sans Serif", "Sans Serif");
                    selectList.Add("Tahoma", "Tahoma");
                    selectList.Add("Times", "Times New Roman");
                    selectList.Add("Verdana", "Verdana");
                    selectList.Add("Wingdings", "Wingdings");
                    break;
                case "Size":
                    selectList.Add("Size", "");
                    selectList.Add("Very Small", "1");
                    selectList.Add("Smaller", "2");
                    selectList.Add("Small", "3");
                    selectList.Add("Medium", "4");
                    selectList.Add("Large", "5");
                    selectList.Add("Larger", "6");
                    selectList.Add("Very Large", "7");
                    break;
                default:
                    throw new HttpException(String.Format("Invalid select list name: {0}", name));
            }

            return selectList;
        }

        protected ButtonInfo CreateButtonInfo(string name)
        {
            switch (name)
            {
                case "ForeColor" :
                    return new ButtonInfo(this, name, "Text Color");
                case "BackColor" :
                    return new ButtonInfo(this, name, "Background Color");
                case "Bold":
                    return new ButtonInfo(this, name, "Bold");
                case "Italic":
                    return new ButtonInfo(this, name, "Italic");
                case "Underline":
                    return new ButtonInfo(this, name, "Underline");
                case "Left":
                    return new ButtonInfo(this, name, "Left");
                case "Center":
                    return new ButtonInfo(this, name, "Center");
                case "Right":
                    return new ButtonInfo(this, name, "Right");
                case "Justify":
                    return new ButtonInfo(this, name, "Justify");
                case "OrderedList":
                    return new ButtonInfo(this, name, "Ordered List");
                case "BulletedList":
                    return new ButtonInfo(this, name, "Bulleted List");
                case "Indent":
                    return new ButtonInfo(this, name, "Indent");
                case "Outdent":
                    return new ButtonInfo(this, name, "Outdent");
                case "Rule":
                    return new ButtonInfo(this, name, "Horizontal Rule");
                case "Subscript":
                    return new ButtonInfo(this, name, "Subscript");
                case "Superscript":
                    return new ButtonInfo(this, name, "Superscript");
                case "Link":
                    return new ButtonInfo(this, name, "Hyperlink");
                case "Image":
                    return new ButtonInfo(this, name, "Image");
                case "Design":
                    ButtonInfo designButtonInfo = new ButtonInfo(this, name, "Design Mode");
                    designButtonInfo.Enabled = ToolbarElement.EnabledType.Html;
                    designButtonInfo.Dependency = ToolbarElement.DependencyType.None;
                    designButtonInfo.DisableMethod = ToggleMode == ToggleModeType.ToggleButton ? ToolbarElement.DisableMethodType.Hide : ToolbarElement.DisableMethodType.Opacity;
                    return designButtonInfo;
                case "Html":
                    string htmlToolTip = this.HtmlModeEditable ? "Html Mode" : "View Html";
                    ButtonInfo htmlButtonInfo = new ButtonInfo(this, name, htmlToolTip);
                    htmlButtonInfo.Enabled = ToolbarElement.EnabledType.Design;
                    htmlButtonInfo.Dependency = ToolbarElement.DependencyType.None;
                    htmlButtonInfo.DisableMethod = ToggleMode == ToggleModeType.ToggleButton ? ToolbarElement.DisableMethodType.Hide : ToolbarElement.DisableMethodType.Opacity;
                    return htmlButtonInfo;
                case "View":
                    ButtonInfo viewButtonInfo = new ButtonInfo(this, name, "View Mode");
                    viewButtonInfo.Enabled = ToolbarElement.EnabledType.Html;
                    viewButtonInfo.Dependency = ToolbarElement.DependencyType.None;
                    viewButtonInfo.DisableMethod = ToggleMode == ToggleModeType.ToggleButton ? ToolbarElement.DisableMethodType.Hide : ToolbarElement.DisableMethodType.Opacity;
                    return viewButtonInfo;
                case "Save":
                    ButtonInfo saveButtonInfo = new ButtonInfo(this, name, "Save");
                    saveButtonInfo.Enabled = ToolbarElement.EnabledType.Never;
                    saveButtonInfo.Dependency = ToolbarElement.DependencyType.Editable;
                    return saveButtonInfo;
                case "New":
                    ButtonInfo newButtonInfo = new ButtonInfo(this, name, "New");
                    newButtonInfo.Enabled = ToolbarElement.EnabledType.Always;
                    newButtonInfo.Dependency = ToolbarElement.DependencyType.Editable;
                    return newButtonInfo;
                default:
                    throw new HttpException(String.Format("Invalid button name: {0}", name));
            }
        }
        
        protected ToolbarInfo CreateTopToolbarInfo()
        {
            ToolbarInfo toolbarInfo = new ToolbarInfo("Top");

            string[] toolbarSelectLists = GetStringList(this.ToolbarSelectLists);

            foreach (string name in toolbarSelectLists)
            {
                SelectInfo.SelectList selectList = CreateSelectList(name);
                toolbarInfo.Items.Add(new ToolbarInfo.ToolbarInfoItem(ToolbarInfo.ElementType.Select, new SelectInfo(this, name, selectList, "Do" + name, false)));
            }

            List<ButtonInfo> buttons = new List<ButtonInfo>();

            string[] toolbarButtonsTop = GetStringList(this.ToolbarButtonsTop);

            foreach (string name in toolbarButtonsTop)
            {
                buttons.Add(CreateButtonInfo(name));
            }

            toolbarInfo.Items.Add(new ToolbarInfo.ToolbarInfoItem(ToolbarInfo.ElementType.Buttons, new ButtonBarInfo("Buttons", buttons)));

            return toolbarInfo;
        }

        protected void AddToggleModeButtons(List<String> toolbarButtonsBottom)
        {
            switch (this.ToggleMode)
            {
                case ToggleModeType.ToggleButton :
                case ToggleModeType.Buttons :
                    break;
                default :
                    return;
            }

            List<String> toolbarButtonsTop = new List<String>(GetStringList(this.ToolbarButtonsTop));
            
            string designModeButtonName = this.DesignModeEditable ? "Design" : "View";
            bool hasDesignModeButton = toolbarButtonsBottom.Contains(designModeButtonName) || toolbarButtonsTop.Contains(designModeButtonName);
            bool hasHtmlModeButton = toolbarButtonsBottom.Contains("Html") || toolbarButtonsTop.Contains("Html");
            
            if (!hasHtmlModeButton)
            {
                toolbarButtonsBottom.Insert(0, "Html");
            }

            if (!hasDesignModeButton)
            {
                toolbarButtonsBottom.Insert(0, designModeButtonName);
            }
        }
        
        protected ToolbarInfo CreateBottomToolbarInfo()
        {
            ToolbarInfo toolbarInfo = new ToolbarInfo("Bottom");

            List<String> toolbarButtonsBottom = new List<String>(GetStringList(this.ToolbarButtonsBottom));
            AddToggleModeButtons(toolbarButtonsBottom);
            
            List<ButtonInfo> buttons = new List<ButtonInfo>();
            
            foreach (string name in toolbarButtonsBottom)
            {
                buttons.Add(CreateButtonInfo(name));
            }

            toolbarInfo.Items.Add(new ToolbarInfo.ToolbarInfoItem(ToolbarInfo.ElementType.Buttons, new ButtonBarInfo("Buttons", buttons)));

            return toolbarInfo;
        }

        protected void PackToolbarInfoList(List<ToolbarInfo> toolbarInfoList)
        {
            for (int i = toolbarInfoList.Count - 1; i >= 0; i--)
            {
                if (toolbarInfoList[i].Deleted) toolbarInfoList.RemoveAt(i);
            }
        }
        
        protected ToolbarInfo[] CreateToolbarInfoList()
        {
            List<ToolbarInfo> toolbarInfoList = new List<ToolbarInfo>();

            toolbarInfoList.Add(CreateTopToolbarInfo());
            toolbarInfoList.Add(CreateBottomToolbarInfo());

            OnCreateToolbarInfo(new CreateToolbarInfoEventArgs(this, toolbarInfoList));
            PackToolbarInfoList(toolbarInfoList);

            return toolbarInfoList.ToArray();
        }

        protected HtmlSelect CreateHtmlSelect(SelectInfo selectInfo, string previousType, string baseKey, ToolbarControlList toolbarControlList)
        {
            HtmlSelect htmlSelect = new HtmlSelect();
            toolbarControlList.Add(baseKey + "." + selectInfo.Name, htmlSelect);

            htmlSelect.Style["font-family"] = "Verdana";
            htmlSelect.Style["font-size"] = "8pt";
            htmlSelect.Style["width"] = "100px";
            htmlSelect.Style["vertical-align"] = "middle";
            htmlSelect.Style["margin-right"] = "2px";
            htmlSelect.Style["display"] = IsToolbarElementHidden(selectInfo) ? "none" : "inline";

            foreach (string key in selectInfo.Items.Keys)
            {
                ListItem option = new ListItem(key, selectInfo.Items[key]);
                htmlSelect.Items.Add(option);
            }

            htmlSelect.Attributes["onchange"] = selectInfo.OnChange;

            if (previousType == "None")
            {
                htmlSelect.Style["margin-left"] = ToolbarPadding.ToString() + "px";
            }

            return htmlSelect;
        }

        protected bool IsToolbarElementHidden(ToolbarElement toolbarElement)
        {
            if (toolbarElement.DisableMethod != ToolbarElement.DisableMethodType.Hide) return false;
            if (toolbarElement.Enabled == ToolbarElement.EnabledType.Never) return true;
            if (toolbarElement.Enabled == ToolbarElement.EnabledType.Always) return false;
            
            return toolbarElement.Enabled.ToString() != this.InitialMode.ToString();
        }
        
        protected HtmlGenericControl CreateButtonBar(ButtonBarInfo buttonBarInfo, string previousType, string baseKey, ToolbarControlList toolbarControlList)
        {
            HtmlGenericControl buttonBar = new HtmlGenericControl("div");
            buttonBar.Style["display"] = IsToolbarElementHidden(buttonBarInfo) ? "none" : "inline";

            baseKey += "." + buttonBarInfo.Name;
            toolbarControlList.Add(baseKey, buttonBar);

            foreach (ButtonInfo buttonInfo in buttonBarInfo.Buttons)
            {
                HtmlImage img = new HtmlImage();
                toolbarControlList.Add(baseKey + "." + buttonInfo.Name, img);
                if (!DesignMode)
                    img.Src = buttonInfo.ImageSrc;
                else
                {
                    string resource = buttonInfo.Resource;
                    img.Src = GetLocalResourceUrl(resource, buttonInfo.Name + ".gif");
                }
                img.Alt = buttonInfo.ToolTip;
                img.Style["vertical-align"] = "middle";
                img.Style["border"] = "1px solid";
                img.Style["border-color"] = this.ToToolbarColor;
                img.Style["display"] = IsToolbarElementHidden(buttonInfo) ? "none" : "inline";
                img.Attributes["title"] = buttonInfo.ToolTip;
                img.Attributes["onmouseover"] = buttonInfo.OnMouseOver;
                img.Attributes["onmouseout"] = buttonInfo.OnMouseOut;
                img.Attributes["onclick"] = buttonInfo.OnClick;

                buttonBar.Controls.Add(img);
            }

            switch (previousType)
            {
                case "None" :
                    buttonBar.Style["margin-left"] = ToolbarPadding.ToString() + "px";
                    break;
                case "Select" :
                    buttonBar.Style["margin-left"] = "8px";
                    break;
                default :
                    break;
            }
            
            return buttonBar;
        }

        protected Toolbar CreateToolbar(ToolbarInfo toolbarInfo, ToolbarControlList toolbarControlList, bool first, bool last)
        {
            Toolbar toolbar = new Toolbar(this);
            
            string key = toolbarInfo.Name;
            toolbarControlList.Add(key, toolbar);

            if (first)
            {
                toolbar.Style["position"] = "relative";
                toolbar.Style["left"] = "0px";
                toolbar.Style["top"] = "0px";
                toolbar.Style["padding-top"] = ToolbarPadding.ToString() + "px";

                if (this.ToolbarDocked)
                {
                    toolbar.Controls.Add(CreateRoundedCorner(true, true, ToBackColor));
                    toolbar.Controls.Add(CreateRoundedCorner(false, true, ToBackColor));
                }
            }

            if (last)
            {
                int paddingBottom = this.ToolbarDocked ? AdditionalToolbarPadding : ToolbarPadding;
                toolbar.Style["padding-bottom"] = paddingBottom.ToString() + "px";
            }

            toolbar.Style["display"] = IsToolbarElementHidden(toolbarInfo) ? "none" : "block";

            string previousType = "None";

            foreach (ToolbarInfo.ToolbarInfoItem item in toolbarInfo.Items)
            {
                switch (item.Type)
                {
                    case ToolbarInfo.ElementType.Select :
                        SelectInfo selectInfo = (SelectInfo)item.Element;
                        toolbar.Controls.Add(CreateHtmlSelect(selectInfo, previousType, key, toolbarControlList));
                        break;
                    case ToolbarInfo.ElementType.Buttons :
                        ButtonBarInfo buttonBarInfo = (ButtonBarInfo)item.Element;
                        toolbar.Controls.Add(CreateButtonBar(buttonBarInfo, previousType, key, toolbarControlList));
                        break;
                    default :
                        continue;
                }

                previousType = item.Type.ToString();
            }

            toolbar.Controls.Add(new LiteralControl(Environment.NewLine));

            return toolbar;
        }

        protected HtmlGenericControl CreateToolbarContainer()
        {
            HtmlGenericControl toolbarContainer = new HtmlGenericControl("div");
            
            if (!ToolbarDocked)
            {
                toolbarContainer.Style["position"] = "absolute";

                if (ToolbarClass.Length > 0)
                {
                    toolbarContainer.Attributes["class"] = ToolbarClass;
                }
            }

            return toolbarContainer;
        }

        protected void CreateToolbars(ToolbarInfo[] toolbarInfoList)
        {
            this.toolbarContainer = CreateToolbarContainer();

            List<Toolbar> toolbars = new List<Toolbar>();
            ToolbarControlList toolbarControlList = new ToolbarControlList();

            for (int i = 0; i < toolbarInfoList.Length; i++ )
            {
                bool first = i == 0;
                bool last = toolbarInfoList.Length == i + 1;
                
                toolbars.Add(CreateToolbar(toolbarInfoList[i], toolbarControlList, first, last));
            }

            foreach (Toolbar toolbar in toolbars)
                this.toolbarContainer.Controls.Add(toolbar);

            this.Controls.Add(this.toolbarContainer);
            this.ToolbarIDs = GetToolbarIDs(toolbars.ToArray());
            this.ToolbarData = GetToolbarData(toolbarInfoList, toolbarControlList);
        }

        protected string GetToolbarData(ToolbarInfo[] toolbarInfoList, ToolbarControlList toolbarControlList)
        {
            string toolbarData = String.Empty;

            foreach (ToolbarInfo toolbarInfo in toolbarInfoList)
            {
                if (toolbarData.Length > 0) 
                    toolbarData += ";";
                
                toolbarData += toolbarInfo.GetToolbarData(toolbarControlList);
            }

            return toolbarData;
        }

        protected HtmlGenericControl CreateEditorArea(string id, out HtmlGenericControl editor, string editorID, string editorScrolling)
        {
            HtmlGenericControl area = new HtmlGenericControl("div");
            
            area.ID = id;
            if (Width.Type == UnitType.Percentage)
                area.Style["width"] = (Width.Value - 1).ToString() + "%";
            else
                area.Style["width"] = EditorWidth.ToString() + "px";
            area.Style["height"] = EditorHeight.ToString() + "px";
            area.Style["border"] = "solid " + EditorBorderSize.ToString() + "px " + ToEditorBorderColor;
            area.Style["font-family"] = _editorFontFamily;

            editor = new HtmlGenericControl("iframe");
            
            editor.ID = editorID;
            editor.Attributes["frameBorder"] = "0";
            if (Width.Type == UnitType.Percentage)
                editor.Attributes["width"] = (Width.Value -1).ToString() + "%";
            else
            editor.Attributes["width"] = EditorWidth.ToString() + "px";
            editor.Attributes["height"] = EditorHeight.ToString() + "px";
            editor.Attributes["scrolling"] = editorScrolling;
            editor.Style["background-color"] = ToEditorBackColor;
            editor.Style["color"] = ToEditorForeColor;
            editor.Style["font-family"] = _editorFontFamily;

            area.Controls.Add(editor);

            return area;
        }

        protected HtmlGenericControl CreateHtmlArea()
        {
            htmlArea = CreateEditorArea("contentText", out htmlEditor, "htmlEditor", "no");
            htmlArea.Style["display"] = this.InitialMode == Mode.Html ? "block" : "none";

            return htmlArea;
        }

        protected HtmlGenericControl CreateDesignArea()
        {
            designArea = CreateEditorArea("contentHtml", out designEditor, "designEditor", "auto");
            designArea.Style["display"] = this.InitialMode == Mode.Design ? "block" : "none";
            
            return designArea;
        }

        protected HtmlGenericControl CreateTab(int tabNumber, string name, string toolTip, string switchMode, string imgName, out HtmlImage img, out Label label)
        {
            int tabIndex = tabNumber - 1;
            int tabWidth = TabWidth;
            int tabSpacing = 2;
            int leftPosition = ToolbarPadding + (tabWidth * tabIndex) + (tabSpacing * tabIndex);
            bool selected = this.InitialMode.ToString() == switchMode;
            
            HtmlGenericControl tab = new HtmlGenericControl("div");
            tab.Controls.Add(CreateRoundedCorner(true, false, ToBackColor));
            tab.Controls.Add(CreateRoundedCorner(false, false, ToBackColor));
            tab.Style["height"] = "30px";
            tab.Style["width"] = tabWidth.ToString() + "px";
            tab.Style["background-color"] = selected ? ToSelectedTabBackColor : ToTabBackColor;
            tab.Style["cursor"] = "pointer";
            tab.Style["position"] = "absolute";
            tab.Style["left"] = leftPosition.ToString() + "px";
            tab.Style["top"] = "0px";
            tab.Style["font-weight"] = selected ? "bold" : "normal";
            tab.Attributes["title"] = toolTip;
            tab.Attributes["onmouseover"] = GetSafeScriptMethod("TabOver(this)");
            tab.Attributes["onmouseout"] = GetSafeScriptMethod("TabOut(this)");
            tab.Attributes["onclick"] = GetSafeScriptMethod("SwitchMode(this, '" + switchMode + "')");

            img = new HtmlImage();
            string resource = string.Format("Winthusiasm.HtmlEditor.Images.{0}.gif", imgName);
            img.Src = GetWebResourceUrl(resource, imgName + ".gif");
            img.Alt = this.DesignMode ? String.Empty : toolTip;
            img.Style["float"] = "left";
           
            tab.Controls.Add(img);

            label = new Label();
            label.Text = name;
            label.Style["float"] = "left";

            tab.Controls.Add(label);

            return tab;
        }

        private string GetLocalResourceUrl(string resource, string imgName)
        {
            Assembly asm = Assembly.GetExecutingAssembly();
            Stream s = asm.GetManifestResourceStream(resource);
            string saveTo = Path.GetTempPath() + imgName;
            // create a write stream
            FileStream writeStream = new FileStream(saveTo, FileMode.Create, FileAccess.Write);
            // write to the stream
            ReadWriteStream(s, writeStream);
            return saveTo;
        }

        // readStream is the stream you need to read
        // writeStream is the stream you want to write to
        private void ReadWriteStream(Stream readStream, Stream writeStream)
        {
            int Length = 256;
            Byte[] buffer = new Byte[Length];
            int bytesRead = readStream.Read(buffer, 0, Length);
            // write the required bytes
            while (bytesRead > 0)
            {
                writeStream.Write(buffer, 0, bytesRead);
                bytesRead = readStream.Read(buffer, 0, Length);
            }
            readStream.Close();
            writeStream.Close();
        }

        protected HtmlGenericControl CreateTabbar()
        {
            HtmlGenericControl tabbar = new HtmlGenericControl("div");
            tabbar.Style["width"] = this.Width.ToString();
            tabbar.Style["height"] = "40px";
            tabbar.Style["font-family"] = "Verdana";
            tabbar.Style["font-size"] = "8pt";
            tabbar.Style["color"] = ToTabForeColor;
            tabbar.Style["position"] = "relative";
            tabbar.Style["left"] = "0px";
            tabbar.Style["top"] = "0px";
            tabbar.Style["display"] = this.ToggleMode == ToggleModeType.Tabs ? "block" : "none";

            int tabNumber = 0;
            
            HtmlImage designImg;
            Label designLabel;

            if (this.DesignModeEditable)
            {
                designTab = CreateTab(++tabNumber, "Design", "Design Mode", "Design", "design", out designImg, out designLabel);
                designTab.ID = "designTab";
                designImg.Style["margin-left"] = this.DesignMode ? "0px" : "5px";
                designImg.Style["margin-top"] = this.DesignMode ? "0px" : "4px";
                designLabel.Style["margin-left"] = "2px";
                designLabel.Style["margin-top"] = "8px";

                tabbar.Controls.Add(designTab);
            }

            HtmlImage htmlImg;
            Label htmlLabel;
            string htmlToolTip = this.HtmlModeEditable ? "Html Mode" : "View Html";
            htmlTab = CreateTab(++tabNumber, "Html", htmlToolTip, "Html", "html", out htmlImg, out htmlLabel);
            htmlTab.ID = "htmlTab";
            htmlImg.Style["margin-left"] = this.DesignMode ? "0px" : "7px";
            htmlImg.Style["margin-top"] = this.DesignMode ? "0px" : "5px";
            htmlLabel.Style["margin-left"] = "2px";
            htmlLabel.Style["margin-top"] = "8px";

            tabbar.Controls.Add(htmlTab);

            if (!this.DesignModeEditable)
            {
                designTab = CreateTab(++tabNumber, "View", "View Mode", "Design", "view", out designImg, out designLabel);
                designTab.ID = "viewTab";
                designImg.Style["margin-left"] = this.DesignMode ? "0px" : "10px";
                designImg.Style["margin-top"] = this.DesignMode ? "0px" : "5px";
                designLabel.Style["margin-left"] = "2px";
                designLabel.Style["margin-top"] = "8px";

                tabbar.Controls.Add(designTab);
            }

            return tabbar;
        }

        protected HtmlGenericControl CreateUpdateArea()
        {
            HtmlGenericControl updateArea = new HtmlGenericControl("div");
            updateArea.Style["display"] = "none";

            htmlencodedText = new HiddenField();
            htmlencodedText.ID = "htmlencodedText";
            htmlencodedText.Value = ClientEncode(this.text);

            saveButton = new Button();
            saveButton.ID = "SaveButton";
            saveButton.Click += new EventHandler(SaveButton_Click);
            saveButton.UseSubmitBehavior = false;
            
            updatePanel = new UpdatePanel();
            updatePanel.ID = "updatePanel";
            updatePanel.UpdateMode = UpdatePanelUpdateMode.Conditional;
            updatePanel.RenderMode = UpdatePanelRenderMode.Inline;

            updatePanel.ContentTemplateContainer.Controls.Add(htmlencodedText);
            updatePanel.ContentTemplateContainer.Controls.Add(saveButton);
            
            updateArea.Controls.Add(updatePanel);

            return updateArea;
        }

        void SaveButton_Click(object sender, EventArgs e)
        {
            OnSave(new SaveEventArgs(this));
        }

        protected void CalculateDimensions(int numToolbars)
        {
            int w = (int)this.Width.Value;
            int h = (int)this.Height.Value;
            int tabbarHeight = this.ToggleMode == ToggleModeType.Tabs ? TabbarHeight : 0;
            int toolbarHeight = this.ToolbarDocked ? (ToolbarHeight * numToolbars) + (ToolbarPadding + AdditionalToolbarPadding) : 0;
            
            EditorWidth = w - (EditorBorderSize * 2);
            EditorHeight = h - (EditorBorderSize * 2) - toolbarHeight - tabbarHeight;
        }
        
        protected override void CreateChildControls()
        {
            ToolbarInfo[] toolbarInfoList = CreateToolbarInfoList();
            CalculateDimensions(toolbarInfoList.Length);

            CreateToolbars(toolbarInfoList);
            
            this.Controls.Add(CreateHtmlArea());
            if (!this.DesignMode) 
                this.Controls.Add(CreateDesignArea());
            this.Controls.Add(CreateTabbar());
            this.Controls.Add(CreateUpdateArea());

            base.CreateChildControls();
        }

        protected override void OnPreRender(EventArgs e)
        {
            SetIDs();
            
            if (!this.DesignMode)
            {
                // Test for ScriptManager and register if it exists.
                sm = ScriptManager.GetCurrent(Page);

                if (sm == null)
                    throw new HttpException("A ScriptManager control must exist on the current page.");

                sm.RegisterScriptControl(this);
                
                // If updatePanel was manually updated, register a dataitem.
                if (sm.IsInAsyncPostBack && updateHtml)
                    sm.RegisterDataItem(htmlencodedText, "Updated");
            }

            base.OnPreRender(e);
        }

        protected string GetToolbarIDs(Toolbar[] toolbars)
        {
            string toolbarIDs = String.Empty;
            foreach (Toolbar toolbar in toolbars)
            {
                if (toolbarIDs.Length > 0) toolbarIDs += ",";
                toolbarIDs += toolbar.ClientID;
            }

            return toolbarIDs;
        }
        
        protected void SetIDs()
        {
            HtmlEncodedTextID = htmlencodedText.ClientID;
            HtmlEditorID = htmlEditor.ClientID;
            DesignEditorID = designEditor.ClientID;
            ToolbarContainerID = toolbarContainer.ClientID;
            HtmlAreaID = htmlArea.ClientID;
            DesignAreaID = designArea.ClientID;
            HtmlTabID = htmlTab.ClientID;
            DesignTabID = designTab.ClientID;
            SaveButtonID = saveButton.ClientID;
        }

        protected override void Render(HtmlTextWriter writer)
        {
            if (!this.DesignMode)
                sm.RegisterScriptDescriptors(this);

            base.Render(writer);
        }

        protected virtual IEnumerable<ScriptReference> GetScriptReferences()
        {
            ScriptReference htmlEditorReference = new ScriptReference("Winthusiasm.HtmlEditor.HtmlEditor.js", "Winthusiasm.HtmlEditor");
            return new ScriptReference[] { htmlEditorReference };
        }

        protected virtual IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {

            ScriptControlDescriptor descriptor = new ScriptControlDescriptor("Winthusiasm.HtmlEditor", this.ClientID);
            descriptor.AddProperty("htmlencodedTextID", this.HtmlEncodedTextID);
            descriptor.AddProperty("htmlEditorID", this.HtmlEditorID);
            descriptor.AddProperty("designEditorID", this.DesignEditorID);
            descriptor.AddProperty("htmlAreaID", this.HtmlAreaID);
            descriptor.AddProperty("designAreaID", this.DesignAreaID);
            descriptor.AddProperty("htmlTabID", this.HtmlTabID);
            descriptor.AddProperty("designTabID", this.DesignTabID);
            descriptor.AddProperty("saveButtonID", this.SaveButtonID);
            descriptor.AddProperty("toolbarIDs", this.ToolbarIDs);
            descriptor.AddProperty("editorForeColor", ColorTranslator.ToHtml(this.EditorForeColor));
            descriptor.AddProperty("editorBackColor", ColorTranslator.ToHtml(this.EditorBackColor));
            descriptor.AddProperty("toolbarColor", ColorTranslator.ToHtml(this.ToolbarColor));
            descriptor.AddProperty("tabForeColor", ColorTranslator.ToHtml(this.TabForeColor));
            descriptor.AddProperty("tabBackColor", ColorTranslator.ToHtml(this.TabBackColor));
            descriptor.AddProperty("selectedTabBackColor", ColorTranslator.ToHtml(this.SelectedTabBackColor));
            descriptor.AddProperty("tabMouseOverColor", ColorTranslator.ToHtml(this.TabMouseOverColor));
            descriptor.AddProperty("tabbarBackColor", ColorTranslator.ToHtml(this.TabbarBackColor));
            descriptor.AddProperty("buttonMouseOverColor", ColorTranslator.ToHtml(this.ButtonMouseOverColor));
            descriptor.AddProperty("buttonMouseOverBorderColor", ColorTranslator.ToHtml(this.ButtonMouseOverBorderColor));
            descriptor.AddProperty("outputXHTML", this.OutputXHTML);
            descriptor.AddProperty("convertDeprecatedSyntax", this.ConvertDeprecatedSyntax);
            descriptor.AddProperty("convertParagraphs", this.ConvertParagraphs);
            descriptor.AddProperty("replaceNoBreakSpace", this.ReplaceNoBreakSpace);
            descriptor.AddProperty("showModifiedAsterick", this.ShowModifiedAsterick);
            descriptor.AddProperty("toolbarData", this.ToolbarData);
            descriptor.AddProperty("allowedTags", this.AllowedTags);
            descriptor.AddProperty("allowedAttributes", this.AllowedAttributes);
            descriptor.AddProperty("noScriptAttributes", this.NoScriptAttributes);
            descriptor.AddProperty("formatHtmlMode", this.FormatHtmlMode);
            descriptor.AddProperty("newLine", Environment.NewLine);
            descriptor.AddProperty("initialMode", this.InitialMode.ToString());
            descriptor.AddProperty("designModeEditable", this.DesignModeEditable);
            descriptor.AddProperty("htmlModeEditable", this.HtmlModeEditable);
            descriptor.AddProperty("designModeCss", this.DesignModeCss);
            descriptor.AddProperty("textDirection", this.TextDirection.ToString());
            if (!String.IsNullOrEmpty(this.ModifiedChanged)) descriptor.AddEvent("modifiedChanged", this.ModifiedChanged);
            if (!String.IsNullOrEmpty(this.ContextChanged)) descriptor.AddEvent("contextChanged", this.ContextChanged);

            return new ScriptDescriptor[] { descriptor };
        }

        #region IScriptControl Members

        IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()
        {
            return GetScriptDescriptors();
        }

        IEnumerable<ScriptReference> IScriptControl.GetScriptReferences()
        {
            return GetScriptReferences();
        }

        #endregion

        #region Toolbar Classes

        public abstract class ToolbarElement
        {
            private string name;
            private EnabledType enabled;
            private DependencyType dependency;
            private DisableMethodType disableMethod;

            public enum EnabledType
            {
                Design,
                Html,
                Always,
                Never
            }

            public enum DependencyType
            {
                Editable,
                None
            }

            public enum DisableMethodType
            {
                Opacity,
                Hide
            }

            public string Name
            {
                get { return this.name; }
            }

            public EnabledType Enabled
            {
                get { return this.enabled; }
                set { this.enabled = value; }
            }

            public DependencyType Dependency
            {
                get { return this.dependency; }
                set { this.dependency = value; }
            }

            public DisableMethodType DisableMethod
            {
                get { return this.disableMethod; }
                set { this.disableMethod = value; }
            }

            public ToolbarElement(string name)
            {
                this.name = name;
                this.enabled = EnabledType.Always;
                this.dependency = DependencyType.None;
                this.disableMethod = DisableMethodType.Opacity;
            }
        }

        public class SelectInfo : ToolbarElement
        {
            private SelectList items;
            private string onChange;

            public class SelectList : Dictionary<String, String> {}

            public SelectList Items
            {
                get { return items; }
            }

            public string OnChange
            {
                get { return onChange; }
            }

            public SelectInfo(HtmlEditor editor, string name, SelectList items, string onChange, bool externalMethod) : base(name)
            {
                this.items = items;
                this.onChange = externalMethod ? onChange : editor.GetSafeScriptMethod(onChange + "(this.options[this.selectedIndex].value)");
                this.Enabled = EnabledType.Design;
                this.Dependency = DependencyType.Editable;
            }
        }

        public class ButtonInfo : ToolbarElement
        {
            private string imageSrc;
            private string toolTip;
            private string onMouseOver;
            private string onMouseOut;
            private string onClick;
            private string resource;

            public string ImageSrc
            {
                get { return imageSrc; }
            }

            public string ToolTip
            {
                get { return toolTip; }
            }

            public string OnMouseOver
            {
                get { return onMouseOver; }
            }

            public string OnMouseOut
            {
                get { return onMouseOut; }
            }

            public string OnClick
            {
                get { return onClick; }
            }

            public string Resource
            {
                get { return resource; }
                set { resource = value; }
            }

            public ButtonInfo(HtmlEditor editor, string name, string toolTip)
                : base(name)
            {
                resource = string.Format("Winthusiasm.HtmlEditor.Images.{0}.gif", name.ToLower());
                this.imageSrc = editor.GetWebResourceUrl(resource, name + ".gif");
                this.toolTip = toolTip;
                this.onMouseOver = editor.GetSafeScriptMethod("MouseOver(this)");
                this.onMouseOut = editor.GetSafeScriptMethod("MouseOut(this)");
                this.onClick = editor.GetSafeScriptMethod("Do" + name + "()");
                this.Enabled = EnabledType.Design;
                this.Dependency = DependencyType.Editable;
            }

            public ButtonInfo(HtmlEditor editor, string name, string imageSrc, string toolTip, string onClick, bool externalMethod)
                : base(name)
            {
                this.imageSrc = imageSrc;
                this.toolTip = toolTip;
                this.onMouseOver = editor.GetSafeScriptMethod("MouseOver(this)");
                this.onMouseOut = editor.GetSafeScriptMethod("MouseOut(this)");
                this.onClick = externalMethod ? onClick : editor.GetSafeScriptMethod(onClick);
            }
        }

        public class ButtonBarInfo : ToolbarElement
        {
            private List<ButtonInfo> buttons;

            public List<ButtonInfo> Buttons
            {
                get { return buttons; }
            }

            public ButtonBarInfo(string name, List<ButtonInfo> buttons)
                : base(name)
            {
                this.buttons = buttons;
            }
        }

        public class ToolbarInfo : ToolbarElement
        {
            private List<ToolbarInfoItem> items = new List<ToolbarInfoItem>();
            private bool deleted = false;

            public class ToolbarInfoItem
            {
                private ElementType type;
                private ToolbarElement element;

                public ElementType Type
                {
                    get { return type; }
                }

                public ToolbarElement Element
                {
                    get { return element; }
                }

                public ToolbarInfoItem(ElementType type, ToolbarElement element)
                {
                    this.type = type;
                    this.element = element;
                }
            }

            public enum ElementType
            {
                Select,
                Buttons
            }

            public List<ToolbarInfoItem> Items
            {
                get { return items; }
            }

            public bool Deleted
            {
                get { return deleted; }
            }

            public void Delete()
            {
                this.deleted = true;
            }

            public ToolbarElement FindElement(string name)
            {
                foreach (ToolbarInfoItem item in Items)
                {
                    if (item.Element.Name == name) return item.Element;
                }

                return null;
            }

            public string GetToolbarData(ToolbarControlList toolbarControlList)
            {
                string key = Name;
                string toolbarData = GetToolbarElementData(this, key, toolbarControlList);
                string previousType = "None";

                foreach (ToolbarInfoItem item in Items)
                {
                    switch (item.Type)
                    {
                        case ElementType.Select :
                            SelectInfo selectInfo = (SelectInfo)item.Element;
                            toolbarData += GetSelectData(selectInfo, previousType, key, toolbarControlList);
                            previousType = "Select";
                            break;
                        case ElementType.Buttons :
                            ButtonBarInfo buttonBarInfo = (ButtonBarInfo)item.Element;
                            toolbarData += GetButtonBarData(buttonBarInfo, previousType, key, toolbarControlList);
                            previousType = "Buttons";
                            break;
                        default :
                            continue;
                    }
                }

                return toolbarData;
            }

            protected string GetSelectData(SelectInfo selectInfo, string previousType, string baseKey, ToolbarControlList toolbarControlList)
            {
                string key = baseKey + "." + selectInfo.Name;
                string selectData = previousType == "None" ? "$" : "|";
                selectData += "Select?" + GetToolbarElementData(selectInfo, key, toolbarControlList);

                return selectData;
            }

            protected string GetButtonBarData(ButtonBarInfo buttonBarInfo, string previousType, string baseKey, ToolbarControlList toolbarControlList)
            {
                string key = baseKey + "." + buttonBarInfo.Name;
                string buttonBarData = previousType == "None" ? "$" : "|";
                buttonBarData += "Buttons?" + GetToolbarElementData(buttonBarInfo, key, toolbarControlList);
                
                string previousButtonBarType = "None";
                
                foreach (ButtonInfo buttonInfo in buttonBarInfo.Buttons)
                {
                    buttonBarData += GetButtonData(buttonInfo, previousButtonBarType, key, toolbarControlList);
                    previousButtonBarType = "Button";
                }

                return buttonBarData;
            }

            protected string GetButtonData(ButtonInfo buttonInfo, string previousButtonBarType, string baseKey, ToolbarControlList toolbarControlList)
            {
                string key = baseKey + "." + buttonInfo.Name;
                string buttonData = previousButtonBarType == "None" ? "=" : ",";
                buttonData += GetToolbarElementData(buttonInfo, key, toolbarControlList);

                return buttonData;
            }

            protected string GetToolbarElementData(ToolbarElement toolbarElement, string key, ToolbarControlList toolbarControlList)
            {
                return toolbarElement.Name + "@" + toolbarElement.Enabled.ToString() + "#" + toolbarElement.Dependency.ToString() + "#" + toolbarElement.DisableMethod.ToString() + "#" + toolbarControlList[key].ClientID;
            }

            public ToolbarInfo(string name) : base(name)
            {
            }
        }

        public class Toolbar : HtmlGenericControl
        {
            public Toolbar(HtmlEditor editor)
                : base("div")
            {
                if (editor.ToolbarDocked)
                {
                    this.Style["width"] = editor.Width.ToString();
                }

                this.Style["height"] = editor.ToolbarHeight.ToString() + "px";
                this.Style["background-color"] = editor.ToToolbarColor;
            }
        }

        public class ToolbarControlList : Dictionary<String, Control> {}

        #endregion

        #region CreateToolbarInfo EventHandler Classes

        public sealed class CreateToolbarInfoEventArgs : EventArgs
        {
            private HtmlEditor editor;
            private List<ToolbarInfo> toolbarInfoList;

            public List<ToolbarInfo> ToolbarInfoList
            {
                get { return toolbarInfoList; }
            }

            private ButtonInfo FindButtonInfo(List<ButtonInfo> buttonList, string name)
            {
                foreach (ButtonInfo buttonInfo in buttonList)
                {
                    if (buttonInfo.Name == name) return buttonInfo;
                }

                return null;
            }

            public void SetButtons(List<ButtonInfo> buttonList, string commaDelimitedButtonNames)
            {
                SetButtons(buttonList, editor.GetStringList(commaDelimitedButtonNames));
            }

            private void SetButtons(List<ButtonInfo> buttonList, string[] names)
            {
                List<ButtonInfo> newList = new List<ButtonInfo>();

                foreach (string name in names)
                {
                    ButtonInfo buttonInfo = FindButtonInfo(buttonList, name);
                    if (buttonInfo == null)
                        throw new HttpException(String.Format("No button named '{0}' was found in the button list", name));

                    newList.Add(buttonInfo);
                }

                buttonList.Clear();
                buttonList.AddRange(newList);
            }

            public void AddButton(List<ButtonInfo> buttonList, ButtonInfo buttonInfo)
            {
                buttonList.Add(buttonInfo);
            }

            public CreateToolbarInfoEventArgs(HtmlEditor editor, List<ToolbarInfo> toolbarInfoList)
            {
                this.editor = editor;
                this.toolbarInfoList = toolbarInfoList;
            }
        }

        private static readonly object CreateToolbarInfoEventKey = new object();
        public delegate void CreateToolbarInfoEventHandler(object sender, CreateToolbarInfoEventArgs e);

        [Category("Behavior")]
        [Description("Event handler called when toolbars are created.")]
        public event CreateToolbarInfoEventHandler CreateToolbarInfo
        {
            add
            {
                Events.AddHandler(CreateToolbarInfoEventKey, value);
            }
            remove
            {
                Events.RemoveHandler(CreateToolbarInfoEventKey, value);
            }
        }

        protected virtual void OnCreateToolbarInfo(CreateToolbarInfoEventArgs e)
        {
            CreateToolbarInfoEventHandler handler = (CreateToolbarInfoEventHandler)Events[CreateToolbarInfoEventKey];
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region Save EventHandler Classes

        public sealed class SaveEventArgs : EventArgs
        {
            private HtmlEditor editor;

            public string Text
            {
                get { return editor.Text; }
            }

            public SaveEventArgs(HtmlEditor editor)
            {
                this.editor = editor;
            }
        }

        private static readonly object SaveEventKey = new object();
        public delegate void SaveEventHandler(object sender, SaveEventArgs e);

        [Category("Behavior")]
        [Description("Event handler called when the Save toolbar button is clicked.")]
        public event SaveEventHandler Save
        {
            add
            {
                Events.AddHandler(SaveEventKey, value);
            }
            remove
            {
                Events.RemoveHandler(SaveEventKey, value);
            }
        }

        protected virtual void OnSave(SaveEventArgs e)
        {
            SaveEventHandler handler = (SaveEventHandler)Events[SaveEventKey];
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion
    }
}
